package cn.demo.common.mybatisFlex;

import cn.demo.common.core.domain.entity.SysRole;
import cn.demo.common.core.domain.model.LoginUser;
import cn.demo.common.utils.*;
import com.mybatisflex.core.dialect.OperateType;
import com.mybatisflex.core.dialect.impl.CommonsDialectImpl;
import com.mybatisflex.core.query.*;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static cn.demo.common.constant.CommonConstants.*;

@Slf4j
public class MyPermissionDialect extends CommonsDialectImpl {

    private final static String[] deptFilterTables = {
            "sys_dept", "sys_user", "sys_role_dept"
    };


    @Override
    public void prepareAuth(QueryWrapper queryWrapper, OperateType operateType) {
        List<QueryTable> queryTables = ObjUtils.clone(CPI.getQueryTables(queryWrapper));
        List<QueryTable> joinTables = CPI.getJoinTables(queryWrapper);
        if (CollectionUtils.isEmpty(queryTables)) {
            return;
        }
        if (CollectionUtils.isNotEmpty(joinTables)){
            queryTables.addAll(joinTables);
        }
        LoginUser loginUser = SecurityUtils.getLoginUser();
        if (null == loginUser || null == loginUser.getUserInfo() || loginUser.isAdmin()) {
            return;
        }
        Boolean dataScope = ServletUtils.getAttr(DATA_SCOPE_ABLE);
        if (null == dataScope || !dataScope) {
            return;
        }
        int i = 0;
        if (OperateType.SELECT.equals(operateType)) {
            // 添加数据权限
            handleSelect(queryWrapper, queryTables);

            // EXISTS语句中添加数据权限
            QueryCondition condition = CPI.getWhereQueryCondition(queryWrapper);
            while (null != condition) {
                if (condition instanceof Brackets ) {
                    condition = ((Brackets) condition).getChildCondition();
                }
                if (condition instanceof OperatorQueryCondition ) {
                    condition = ((OperatorQueryCondition) condition).getChildCondition();
                }
                condition = CPI.getNextCondition(condition);
                if (condition instanceof OperatorSelectCondition){
                    QueryWrapper queryWrapperSub = ((OperatorSelectCondition) condition).getQueryWrapper();
                    List<QueryTable> queryTablesSub = ObjUtils.clone(CPI.getQueryTables(queryWrapperSub));
                    if (CollectionUtils.isEmpty(queryTablesSub)){
                        continue;
                    }
                    List<QueryTable> joinTablesSub = CPI.getJoinTables(queryWrapperSub);
                    if (CollectionUtils.isNotEmpty(joinTablesSub)){
                        queryTablesSub.addAll(joinTablesSub);
                    }
                    handleSelect(queryWrapperSub, queryTablesSub);
                }
            }
        }
        super.prepareAuth(queryWrapper, operateType);
    }


    private void handleSelect(QueryWrapper queryWrapper, List<QueryTable> queryTables) {

        DeptResult deptResult = getDeptResult();
        if (null == deptResult) {
            return;
        }
        Set<String> deptIds = deptResult.deptIds();
        Set<String> customRoleIds = deptResult.customRoleIds();
        for (QueryTable queryTable : queryTables) {
            if (ArrayUtils.contains(deptFilterTables, queryTable.getName())) {
                String table = ObjUtils.defaultIfNull(queryTable.getAlias(), queryTable.getName());
                if (CollectionUtils.isNotEmpty(deptIds) && CollectionUtils.isNotEmpty(customRoleIds)) {
                    queryWrapper.and(StringUtils.format("({}.dept_id in ({}) or {}.dept_id IN (SELECT dept_id FROM sys_role_dept WHERE role_id in ({})))", table, StringUtils.join(deptIds), table, StringUtils.join(customRoleIds)));
                } else if (CollectionUtils.isNotEmpty(deptIds)) {
                    queryWrapper.and(StringUtils.format("{}.dept_id in ({})", table, StringUtils.join(deptIds)));
                } else if (CollectionUtils.isNotEmpty(customRoleIds)) {
                    queryWrapper.and(StringUtils.format("{}.dept_id IN (SELECT dept_id FROM sys_role_dept WHERE role_id in ({}))", table, StringUtils.join(customRoleIds)));
                }
            }
        }
    }


    private static DeptResult getDeptResult() {
        Set<String> deptIds = new HashSet<>();
        Set<String> customRoleIds = new HashSet<>();
        LoginUser loginUser = SecurityUtils.getLoginUser();
        if (ObjUtils.isNull(loginUser) || ObjUtils.isNull(loginUser.getUserInfo()) || loginUser.isAdmin() || CollectionUtils.isEmpty(loginUser.getRoles())) {
            return null;
        }
        List<Integer> conditions = new ArrayList<>();

        for (SysRole role : loginUser.getRoles()) {
            Integer dataScope = role.getDataScope();
            if (!DATA_SCOPE_CUSTOM.equals(dataScope) && conditions.contains(dataScope)) {
                continue;
            }
            if (DATA_SCOPE_ALL.equals(dataScope)) {
                return null;
            } else if (DATA_SCOPE_CUSTOM.equals(dataScope)) {
                customRoleIds.add(role.getRoleId());
            } else if (DATA_SCOPE_DEPT.equals(dataScope)) {
                deptIds.add(loginUser.getDeptId());
            } else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) {
                deptIds.addAll(loginUser.getDeptIds());
            }
            conditions.add(dataScope);
        }


        return new DeptResult(deptIds, customRoleIds);
    }

    private record DeptResult(Set<String> deptIds, Set<String> customRoleIds) {
    }


}